package com.anbinh.doctor.service.volley.toolbox;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.json.JSONObject;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;

/**
 * MultipartRequest - To handle the large file uploads. Extended from
 * JSONRequest. You might want to change to StringRequest based on your response
 * type.
 * 
 * @author Mani Selvaraj
 * 
 */
public class MultiPartRequest<T> extends Request<T> {

    private MultipartEntity entity;
    private String boundary;
    /* To hold the parameter name and the File to upload */
    private Map<String, File> fileUploads = new HashMap<String, File>();

    /* To hold the parameter name and the string content to upload */
    private Map<String, String> stringUploads = new HashMap<String, String>();

    private Map<String, String> headers = new HashMap<String, String>();
    
    private final String mCharset;
    
    private Gson mGson;
    private Class<T> mJavaClass;
    private final Listener<T> mListener;

    /**
     * Creates a new request.
     * 
     * @param method
     *            the HTTP method to use
     * @param url
     *            URL to fetch the JSON from
     * @param jsonRequest
     *            A {@link JSONObject} to post with the request. Null is allowed
     *            and indicates no parameters will be posted along with request.
     * @param listener
     *            Listener to receive the JSON response
     * @param errorListener
     *            Error listener, or null to ignore errors.
     */
    public MultiPartRequest(int method, String url, Class<T> cls, Listener<T> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        boundary = System.currentTimeMillis() + "";
        entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, boundary, Charset.defaultCharset());
        mCharset = null;
        mGson = new GsonBuilder().serializeNulls().create();
        mJavaClass = cls;
        mListener = listener;
    }

    public void addFileUpload(String param, File file) {
        fileUploads.put(param, file);
    }

    public void addStringUpload(String param, String content) {
        stringUploads.put(param, content);
    }

    public Map<String, File> getFileUploads() {
        return fileUploads;
    }

    public Map<String, String> getStringUploads() {
        return stringUploads;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers;
    }

    public void setHeader(String title, String content) {
        headers.put(title, content);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString;
            if(mCharset == null) {
                jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            } else {
                jsonString = new String(response.data, mCharset);
            }
            jsonString = jsonString.replace("\"null\"", "null");
            T parsedGSON = mGson.fromJson(jsonString, mJavaClass);
            return Response.success(parsedGSON,
                    HttpHeaderParser.parseCacheHeaders(response));
            
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException je) {
            return Response.error(new ParseError(je));
        }
    }

    @Override
    public byte[] getBody() {

        buildMultipartEntity();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            entity.writeTo(bos);
        } catch (IOException e) {
            VolleyLog.e("IOException writing to ByteArrayOutputStream");
        }
        return bos.toByteArray();
    }

    private void buildMultipartEntity() {
        for (Entry<String, File> item : fileUploads.entrySet()) {
            entity.addPart(item.getKey(), new FileBody(item.getValue()));
        }
        for (Entry<String, String> item : stringUploads.entrySet()) {
            try {
                entity.addPart(item.getKey(), new StringBody(item.getValue(), Charset.defaultCharset()));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    public String getBodyContentType() {
        return "multipart/form-data; boundary=" + boundary + "; charset=utf-8";
    }

    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }
}